/******************************************************************************************
文件名：main.c
作者：狼狼    版本：V1.0    创建日期：2021.8.2
文件描述：
    主程序。
历史修改记录：
1. 2021-8-2:V1.0 狼狼
创建。
2. 2022-5-11:V1.0 狼狼
代码公开。
******************************************************************************************/
//+------------------------------------------------------------------------------------------+
//|                                          文件说明
//+------------------------------------------------------------------------------------------+
//|      Loice通信协议从机端Demo，通信端口号为1102。
//|      接收并执行来自Loice主机端的指令，支持以下指令：
//|  CMD ID:1 Value:1 开始传输H264-720P@30-CBR编码码流，流式传输，无IDR帧起始限制
//|  CMD ID:4 Value:1 停止传输编码码流。
//|      默认开启Loice KAP，超时将断开连接。
//|      支持IR LED和IRCUT自动切换；支持会话连接状态指示（绿灯表示有会话连接，红灯表示空闲）。
//|      当前支持的可连接会话数量为：1。
//+------------------------------------------------------------------------------------------+
//|                                         头文件包含
//+------------------------------------------------------------------------------------------+
/*|*/#include <stdio.h>
/*|*/#include <string.h>
/*|*/#include <stdlib.h>
/*|*/#include <stdint.h>
/*|*/#include <stdbool.h>
/*|*/#include <signal.h>
/*|*/#include <unistd.h>
//|  
/*|*/#include <sys/socket.h>
/*|*/#include <netinet/in.h>
/*|*/#include <arpa/inet.h>
//|
/*|*/#include "loice_slave_parser.h"
/*|*/#include "internal_data.h"
/*|*/#include "cmd_reg_table.h"
/*|*/#include "tcp_comm.h"
/*|*/#include "tcp_server.h"
/*|*/#include "stream.h"
/*|*/#include "L_GPIO_Peripherals.h"
/*|*/#include "project_debug.h"
//+------------------------------------------------------------------------------------------+
//|                                          全局变量
//+------------------------------------------------------------------------------------------+
/*|*/static bool program_run = true;
//|
/*|*/ST_PARSER *pparser_obj = NULL;
//|
/*|*/int internal_cmd_pipe[2];
//|
/*|*/uint32_t pending_session_list[MAX_SESSION_NUM] = {0}; // [注意]依赖会话ID不从0开始的特性
/*|*/uint16_t pending_session_data_seq_cnt[MAX_SESSION_NUM] = {0}; // 各会话数据计数
//+------------------------------------------------------------------------------------------+
//|  函数名称：tcp_server_1102_handle
//|  功能描述：1102端口连接建立回调函数
//|  参数说明：新TCP连接的套接字描述符
//|  返回值说明：无
//|  备注：
//+------------------------------------------------------------------------------------------+
static void tcp_server_1102_handle(int comm_sock)
{
    int ret = 0;
    ST_TCP_COMM *ptcpcomm_obj = NULL;
    
    if(pparser_obj->session_num < MAX_SESSION_NUM)
    {
        /* 创建套接字通信对象及处理线程 */
        ret = tcp_comm_create(&ptcpcomm_obj, comm_sock);
        if(ret != TCPCOMM_SUCCESS)
        {
            OUTPUT("[MAIN-ERROR]TCP communicate object create failed with %#x!\n", ret);
            close(comm_sock);
        }
        
        pparser_obj->add_session(pparser_obj, ptcpcomm_obj);
    }
    else
    {
        OUTPUT("[MAIN-ERROR]To reach the maximum number of sessions!\n");
        close(comm_sock);
    }
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：pstream_proc_handle
//|  功能描述：H264码流处理函数
//|  参数说明：指向码流包的指针，该码流包长度
//|  返回值说明：无
//|  备注：单包模式。
//+------------------------------------------------------------------------------------------+
void pstream_proc_handle(uint8_t *pstream, int stream_len)
{
    int ret = 0;
    ST_INTERNAL_DATA internal_data = {0};
    
    for(int i = 0; i < MAX_SESSION_NUM; i++)
    {
        if(pending_session_list[i] != 0)
        {
            pending_session_data_seq_cnt[i]++;
            internal_data.data_type        = DATA_NORMAL;                     /* 数据类型为针对指令的回应数据 */
            internal_data.session_id       = pending_session_list[i];         /* 会话ID将决定数据发往哪一个Socket连接 */
            internal_data.data_type_value  = 1;                               /* Demo中数据类型值固定为1 */
            internal_data.msg_seq          = 0;                               /* 使用0序列号屏蔽序列号检查 */
            internal_data.data_seq         = pending_session_data_seq_cnt[i]; /* 从1开始对数据序列号计数 */
            internal_data.data_length      = stream_len;
            internal_data.pdata            = pstream;
            
            ret = pparser_obj->pack_data(pparser_obj, &internal_data);
            if(ret < 0)
            {
                switch(ret)
                {
                    case PARSER_ERROR_UNEXPECT_DATA_LENGTH:
                        OUTPUT("[STREAM_HANDLE]Session TX_BUF full!\n");
                    break;
                    default:/* 若发送失败，且不是缓冲区满，则删除会话停止传输 */
                        pending_session_list[i] = 0;
                        pending_session_data_seq_cnt[i] = 0;
                    break;
                }
            }
            
            //printf("[STREAM_HANDLE]Send Pack:[%d]\n", stream_len);
        }
    }
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：sig_handle
//|  功能描述：信号处理
//|  参数说明：信号值
//|  返回值说明：无
//|  备注：
//+------------------------------------------------------------------------------------------+
static void sig_handle(int signo)
{
	if(SIGINT == signo || SIGTERM == signo)
    {
        program_run = false;
    }
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：main
//|  功能描述：主函数
//|  参数说明：默认
//|  返回值说明：无
//|  备注：
//+------------------------------------------------------------------------------------------+
int main(int argc, const char *argv[])
{
    /* 初始化 */
	signal(SIGINT,  sig_handle); //注册Ctrl+C信号处理函数
	signal(SIGTERM, sig_handle); //注册无参数kill信号处理函数
    
    OUTPUT("[MAIN-INFO]Loice Slave Demo Startup.\n");
    pipe(internal_cmd_pipe);
    L_GPIOs_Init();
    L_GPIO_LED_G_Off();
    L_GPIO_LED_R_Off();
    Stream_Start(pstream_proc_handle);
    
    ST_TCP_SERVER *ptcp_server_obj_1102 = NULL;
    tcp_server_create(&ptcp_server_obj_1102, "0.0.0.0", 1102, "Loice_Slave_V1", tcp_server_1102_handle);
    
    parser_create(&pparser_obj, PARSER_PROTOCOL_1, NULL);
    
    /* 主循环 */
    int ret;
    fd_set readfds;
    struct timeval select_timeout;
    ST_INTERNAL_CMD internal_cmd = {0};
    while(program_run)
    {
        /* 根据解析器是否有会话操控指示灯 */
        if(pparser_obj->session_num == 0)
        {
            L_GPIO_LED_G_Off();
            L_GPIO_LED_R_On();
        }
        else
        {
            L_GPIO_LED_R_Off();
            L_GPIO_LED_G_On();
        }
        
        /* 实现根据光线变化自动控制IR LED和IRCUT */
        if(L_GPIO_LIGHT_DIODE_STATE() == 0)
        {
            // 环境光暗
            L_GPIO_LED_IR_On();
            L_GPIO_IRCUT_On();
        }
        else
        {
            // 环境光亮
            L_GPIO_LED_IR_Off();
            L_GPIO_IRCUT_Off();
        }
        
        /* 使用select轮询内部指令管道 */
        FD_ZERO(&readfds);
        FD_SET(internal_cmd_pipe[0], &readfds);
        select_timeout.tv_sec  = 2;
        select_timeout.tv_usec = 0;
        ret = select(internal_cmd_pipe[0] + 1, &readfds, NULL, NULL, &select_timeout);
        if(ret < 0)
        {
            OUTPUT("[MAIN-WARNING]Internal command pipe select error!\n");
            break;
        }
        else if(ret == 0)
        {
            //continue; // Nothing to do here
        }
        else
        {
            memset(&internal_cmd, 0, sizeof(ST_INTERNAL_CMD));
            ret = read(internal_cmd_pipe[0], (uint8_t *)&internal_cmd, sizeof(ST_INTERNAL_CMD));
            if(ret < 0)
            {
                OUTPUT("[MAIN-WARNING]Read pipe error!\n");
            }
            
            /* TESTPOINT:查找记录表，调用对应的执行回调函数 */
            extern int internal_cmd_match(ENUM_CMD_ID cmd_id, uint16_t cmd_value);
            ret = internal_cmd_match(internal_cmd.cmd_id, internal_cmd.cmd_value);
            if(ret < 0)
            {
                OUTPUT("[MAIN-WARNING]No internal cmd records were found!\n");
                continue;
            }
            if(cmd_reg_table[ret].cmd_process_handle != NULL)
            {
                cmd_reg_table[ret].cmd_process_handle(internal_cmd);
            }
        }
    }
    
    /* 退出操作 */
    tcp_server_destroy(&ptcp_server_obj_1102);
    parser_destroy(&pparser_obj);
    
    close(internal_cmd_pipe[1]);
    close(internal_cmd_pipe[0]);
    Stream_Exit();
    L_GPIO_LED_G_Off();
    L_GPIO_LED_R_Off();
    L_GPIO_LED_IR_Off();
    L_GPIO_IRCUT_Off();
    L_GPIOs_Deinit();
    OUTPUT("[MAIN-INFO]Loice Slave Demo Exit.\n");
    
	return 0;
}
